package edu.nps.ai.agent;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.nps.ai.astar.AStar;
import edu.nps.ai.weapon.Weapon;

/**
 * @author Timothy Riley
 *
 */
public class LabFourAgent extends AbstractAgent {
	/**
	 * Holds true if the red button in the command center has been pushed
	 */
	private List<String> patrolList;
	/**
	 * Holds the energy pattern
	 */
	private Pattern numberPattern;
	/**
	 * Holds the current amount of energy the agent has
	 */
	private Integer energy;
	/**
	 * Holds the weapon currently being held
	 */
	private Weapon weapon;
	/**
	 * Initialize the environment
	 *
	 * @param host
	 *            The host name of the server
	 * @param port
	 *            The port the server is running one
	 * @param userName
	 *            The user name to create in the environment
	 * @param password
	 *            The password for the user created
	 * @param delay
	 *            The delay between commands
	 * @param logFile
	 *            The log file
	 */
	public LabFourAgent(final String host, final Integer port, final String userName, final String password, final Integer delay, final String logFile) {
		initialize(host, port, userName, password, delay, logFile);
		patrolList = new ArrayList<String>();
		patrolList.add("aft"); //Starting from Command Center
		patrolList.add("aft");
		patrolList.add("forward");
		patrolList.add("port");
		patrolList.add("aft");
		patrolList.add("port");
		patrolList.add("aft");
		patrolList.add("forward");
		patrolList.add("starboard");
		patrolList.add("starboard");
		patrolList.add("port");
		patrolList.add("aft");
		patrolList.add("starboard");
		patrolList.add("forward");
		patrolList.add("aft");
		patrolList.add("aft");
		patrolList.add("aft");
		patrolList.add("aft");
		patrolList.add("forward");
		patrolList.add("forward");
		patrolList.add("forward");
		patrolList.add("starboard");
		patrolList.add("forward");
		patrolList.add("port");
		patrolList.add("starboard");
		patrolList.add("starboard");
		patrolList.add("forward");
		patrolList.add("aft");
		patrolList.add("port");
		patrolList.add("forward");
		patrolList.add("port");
		patrolList.add("forward");//Ending in Command Center
		numberPattern = Pattern.compile("(?:[a-zA-Z\\s\\W]*([0-9]*)[a-zA-Z\\s\\W]*)");
	}

	/**
	 * @see edu.nps.ai.agent.Agent#execute()
	 */
	public void execute() {
		listen(); //Reset the buffer
		goToNode("Command Center");
		listen(); //Reset the buffer
		sleep(3000, "Get Ready to Fight");
		send("push red button");
		listen(); //Reset the buffer
		Boolean continuePatrol = true;
		while (continuePatrol) {
			Boolean resetPatrol = false;
			for (String patrol : patrolList) {
				Boolean ready = false;
				Boolean run = false;
				Boolean snit = false;
				String currentEnergy = null;
				send(patrol);
				List<String> output = listen();
				for (String line : output) {
					if (line.contains("energy is now")) {
						currentEnergy = line;
					} else if (line.contains("enough energy to fight")) {
						run = true;
					} else if (line.contains("You can't go that way") || line.contains("You don't have enough energy.")) {
						resetPatrol = true;
						break;
					} else if (line.contains("All Snit have been eliminated.") || line.contains("demonstrated their superiority")) {
						continuePatrol = false;
						break;
				    } else if (line.contains("There are Snit-")) {
						Matcher matcher = numberPattern.matcher(line);
						Integer adults = 0;
						if (matcher.find()) {
							matcher.find();
							String tmp = matcher.group(1);
							adults = Integer.parseInt(tmp);
							if (adults > 3) {
								run = true;
							} else {
								snit = true;
							}
						}
						ready = true;
				    } else if (line.contains("You haven't readied a weapon")) {
				    	if (weapon != null) {
				    		evaluateWeapon(-100); //
				    	}
				    	ready = true;
				    } else if (weapon != null) {
				    	Integer effectiveness = weapon.getEffectiveness();
				    	if (effectiveness == null) {
					    	if (line.contains("Snit are killed") && effectiveness == null) {
					    		evaluateWeapon(100);
					    	} else if (line.contains("Snit is killed") && effectiveness == null) {
								evaluateWeapon(50);
							} else if (line.contains("Snit ignore")  && effectiveness == null) {
								evaluateWeapon(0);
							} else if (line.contains("Snit like") && effectiveness == null) {
								evaluateWeapon(-50);
							} else if (line.contains("Snit love") && effectiveness == null) {
								evaluateWeapon(-100);
							}
				    	} else {
				    		if (weapon.isExpired()) {
				    			log("Weapon has expired");
				    			evaluateWeapon(25); //Still a good weapon just not efficient
				    		}
						}
				    	if (line.contains("I see no")) {
					    	evaluateWeapon(-100); //Lost Weapon
						}
				    }
				}
				if (resetPatrol) {
					log("Reset Patrol");
					sleep(10000, "Reset Patrol");
					goToNode("Command Center");
					break;
				}
				if (!continuePatrol) {
					break;
				}
				if (!run) {
					if (currentEnergy != null) {
						Matcher matcher = numberPattern.matcher(currentEnergy);
						if (matcher.find()) {
							String tmp = matcher.group(1);
							energy = Integer.parseInt(tmp);
							if (energy < 25) {
								sleep(30000, "Energy less than 25 [" + energy + "]");
							} else if (energy < 50) {
								sleep(15000, "Energy less than 50 [" + energy + "]");
							} else if (energy < 75) {
								sleep(10000, "Energy less than 75 [" + energy + "]");
							} else if (energy < 90) {
								sleep(3000, "Energy less than 90 [" + energy + "]");
							}
						}
					}
					if (weapon == null) {
						if (ready) {
							readyWeapon();
						}
					} else {
						if (snit) {
							sleep(3000, "Stay and Fight");
						}
					}
				} else {
					log("Running Away!!");
				}
			}
		}
	}

	/**
	 * Helper method to evaluate a weapons usefulness
	 */
	private void evaluateWeapon(Integer effectiveness) {
		if (effectiveness == null) {
			throw new IllegalArgumentException("The effectiveness is required");
		}
		if (weapon == null) {
			throw new IllegalStateException("The weapon is required");
		}
		log("Process " + weapon + " Effectiveness " + effectiveness);
		weapon.setEffectiveness(effectiveness);
		if (weapon.getSingleUse()) {
			weapon = null;
		} else {
			if (effectiveness < 50) {
				log("Dropping " + weapon);
				weapon.setTimeAcquired(null);
				send("drop " + weapon);
				weapon = null;
			}
		}
		//TMP
		log("********************* WEAPONS *************************");
		Map<String, Weapon> weaponMap = getWeaponMap();
		for (Weapon current : weaponMap.values()) {
			log("WEAPON: " + current + ":" + current.getEffectiveness());
		}
		log("*******************************************************");
	}

	/**
	 * Helper method to ready a weapon
	 */
	private void readyWeapon() {
		log("Ready Weapon");
		String currentLocation = location();
		weapon = effectiveWeapon();
		String location = weapon.getLocation();
		goToNode(location);
		send("get " + weapon);
		send("ready " + weapon);
		Date now = new Date();
		weapon.setTimeAcquired(now.getTime());
		sleep(9000, "Recover from Getting Weapon");
		listen();//Reset Output
		goToNode(currentLocation);
	}

	/**
	 * Helper method to search the output for the phrase "go to X"
	 * Where X is the location to navigate to
	 * @param output
	 * @return The node to navigate to or null if not found
	 */
	private void goToNode(final String toNode) {
		if (toNode == null) {
			throw new IllegalArgumentException("The to node is requried");
		}
		AStar astar = getAstar();
		String currentNode = location();
		List<String> navigationCommands = astar.search(currentNode, toNode);
		Integer index = 0;
		for (String command : navigationCommands) {
			send(command);
			index++;
		}
	}

	/**
	 * Main method to execute the agent
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		if (args.length != 6) {
			throw new IllegalArgumentException("Wrong number of command line arguments!");
		}
		String hostName = args[0];
		Integer port = Integer.parseInt(args[1]);
		String userName = args[2];
		String password = args[3];
		Integer delay = Integer.parseInt(args[4]);
		String logFile = args[5];
		LabFourAgent lab = new LabFourAgent(hostName, port, userName, password, delay, logFile);
		lab.execute();
	}
}
